package top.cardone.security.shiro.realm.impl;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import top.cardone.context.ApplicationContextHolder;
import top.cardone.core.CodeException;
import top.cardone.core.util.func.Func1;
import top.cardone.security.Descipher;
import top.cardone.security.shiro.authc.impl.UsernamePasswordTokenImpl;

import java.util.Map;
import java.util.Set;

/**
 * Created by yht on 16-2-2.
 */
@Log4j2
public class UsernamePasswordRealmImpl extends AuthorizingRealm {
    @Setter
    @Getter
    private String readListRoleFuncName = StringUtils.EMPTY;

    @Setter
    @Getter
    private String readListPermissionFuncName = StringUtils.EMPTY;

    @Setter
    @Getter
    private String readUserFuncName = StringUtils.EMPTY;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordTokenImpl;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //根据用户名查找角色，请根据需求实现
        String username = (String) principals.getPrimaryPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        if (StringUtils.isNotBlank(readListRoleFuncName)) {
            authorizationInfo.setRoles((Set<String>) ApplicationContextHolder.func(Func1.class, func -> func.func(username), readListRoleFuncName));
        }

        if (StringUtils.isNotBlank(readListPermissionFuncName)) {
            authorizationInfo.setStringPermissions((Set<String>) ApplicationContextHolder.func(Func1.class, func -> func.func(username), readListPermissionFuncName));
        }

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordTokenImpl usernamePasswordToken = (UsernamePasswordTokenImpl) token;

        if (usernamePasswordToken.getValidation()) {
            if (org.apache.commons.lang3.StringUtils.isBlank(usernamePasswordToken.getValidationCode())) {
                throw new CodeException("000005", "验证码不能为空");
            }

            if (!org.apache.commons.lang3.StringUtils.equalsIgnoreCase(usernamePasswordToken.getValidationCode(), usernamePasswordToken.getServiceValidationCode())) {
                throw new CodeException("000005", "验证码输入不正确");
            }
        }

        if (usernamePasswordToken.getCredentials() == null || StringUtils.isBlank(usernamePasswordToken.getCredentials().toString())) {
            throw new CodeException("000100", "凭证不能为空值!");
        }

        if (usernamePasswordToken.getPrincipal() == null || StringUtils.isBlank(usernamePasswordToken.getPrincipal().toString())) {
            throw new CodeException("000100", "用户编号不能为空值!");
        }

        String credentials = null;

        if (StringUtils.isNotBlank(readUserFuncName)) {
            Map<String, Object> user = (Map<String, Object>) ApplicationContextHolder.func(Func1.class, func -> func.func(usernamePasswordToken.getPrincipal()), readUserFuncName);

            String slat = MapUtils.getString(user, "slat");
            String password = MapUtils.getString(user, "password");

            if (StringUtils.isBlank(slat)) {
                usernamePasswordToken.setPassword(DigestUtils.md5Hex(new String(usernamePasswordToken.getPassword())).toCharArray());

                credentials = password;
            } else {
                credentials = ApplicationContextHolder.getBean(Descipher.class).decrypt(slat, password);
            }
        }

        //然后进行客户端消息摘要和服务器端消息摘要的匹配
        return new SimpleAuthenticationInfo(usernamePasswordToken.getPrincipal(), credentials, getName());
    }
}